/**
 * @file apf27_call_trusted_fct.S
 *
 * @section descr File description
 *
 * Management of trusted functions
 *
 * @section copyright Copyright
 *
 * Trampoline OS
 *
 * Trampoline is copyright (c) IRCCyN 2005+
 * Copyright ESEO for function and data structures documentation and ARM port
 * Trampoline is protected by the French intellectual property law.
 *
 * This software is distributed under the Lesser GNU Public Licence
 *
 * @section infos File informations
 *
 * $Date: $
 * $Rev: $
 * $Author: $
 * $URL: $
 */

#include "tpl_os_application_def.h"
#include "tpl_asm_definitions.h"

#define OS_START_SEC_CODE
#include "tpl_as_memmap.h"

#if WITH_MEMORY_PROTECTION == YES
.global tpl_call_trusted_function_service
tpl_call_trusted_function_service:
#if TRUSTED_FCT_COUNT > 0
  /* immediatly return E_OS_SERVICEID if trusted function's id is invalid */
  cmp r0, #TRUSTED_FCT_COUNT
  movhs r0, #E_OS_SERVICEID
  movhs pc, lr

  stmfd sp!, {r4}

  /* get the task's return address, so we can push it into process' stack
   * (see kernel enter stack frame documented in tpl_system_call.S) */
  ldr r4, [sp, #(16 + 4)]

  /* switch to user mode to access to current process' registers */
  mrs r3, cpsr
  bic r3, r3, #0x1F
  orr r3, r3, #CPSR_SYS_MODE
  msr cpsr, r3

  /* pushes process' lr and process' return address (in r4) on process' stack */
  stmfd sp!, {r4, lr}
  /* copies ExitTrustedFunction to lr to force this system call on return from trusted function */
  ldr lr, =ExitTrustedFunction

  /* get back to system call mode */
  mrs r3, cpsr
  bic r3, r3, #0x1F
  orr r3, r3, #CPSR_SVC_MODE
  msr cpsr, r3

  /* prepare to run trusted function in privileged mode: we change
   * SPSR (via kernel enter stack frame) as there will be no context switch */
  ldr r3, [sp, #4]
  bic r3, r3, #0x1F
  orr r3, r3, #CPSR_SYS_MODE
  str r3, [sp, #4]

  /* forces kernel enter stack frame to simulate trusted function call */
  ldr r3, =tpl_trusted_fct_table
  ldr r3, [r3, r0, LSL #2] /* get function pointer from trusted functions table */
  str r3, [sp, #(16 + 4)]

  /* increment task's trusted counter */
  ldr r3, =tpl_kern
  ldr r3, [r3, #TPL_KERN_OFFSET_RUNNING]
  ldr r2, [r3, #TPL_PROC_TRUSTED_COUNT] /* according to tpl_os_internal_types.h, trusted_count is an "unsigned int" */
  add r2, r2, #1
  str r2, [r3, #TPL_PROC_TRUSTED_COUNT]

  /* get back to system call handler
   *
   * note that r0 and r1 are left unmodified as they have the two parameters values
   * and they will be restored to the task as if they would contain the returned value
   */
  ldmfd sp!, {r4}
  mov pc, lr
#else /* if TRUSTED_FCT_COUNT == 0 */
  mov r0, #E_OS_SERVICEID
  mov pc, lr
#endif /* TRUSTED_FCT_COUNT == 0*/
#endif /* WITH_MEMORY_PROTECTION == YES */

.global tpl_exit_trusted_function_service
tpl_exit_trusted_function_service:
#if WITH_MEMORY_PROTECTION == YES
#if TRUSTED_FCT_COUNT > 0
  /* decrement task's trusted counter (only if >0, otherwise this is probably an attack) */
  ldr r3, =tpl_kern
  ldr r3, [r3, #TPL_KERN_OFFSET_RUNNING]
  ldr r2, [r3, #TPL_PROC_TRUSTED_COUNT] /* according to tpl_os_internal_types.h, trusted_count is an "unsigned int" */
  cmp r2, #0
  beq cracker_in_action
  sub r2, r2, #1
  str r2, [r3, #TPL_PROC_TRUSTED_COUNT]

  /* give back the original process's rights.
   *
   * many things already done by tpl_mp_kernel_exit but,
   * as there will be no context switch, we force SPSR (via
   * kernel enter stack frame)
   */
  cmp r2, #1
  ldr r3, [sp]
  bic r3, r3, #0x1F
  orrhs r3, r3, #CPSR_SYS_MODE
  orrlo r3, r3, #CPSR_USR_MODE
  str r3, [sp]

  /* switch to user mode to access to current process' registers */
  mrs r3, cpsr
  bic r3, r3, #0x1F
  orr r3, r3, #CPSR_SYS_MODE
  msr cpsr, r3

  /* as ExitTrustedFunction is not really a system call, we remove the LR
   * pushed by the system call wrapper (see in tpl_invoque.s), then we can
   * see directly what have been pushed by tpl_call_trusted_function_service */
  add sp, sp, #4
  /* pops process' lr and process' return address (in r3) on process' stack */
  ldmfd sp!, {r2, lr}

  /* get back to system call mode */
  mrs r3, cpsr
  bic r3, r3, #0x1F
  orr r3, r3, #CPSR_SVC_MODE
  msr cpsr, r3

  /* restores the task's return address, on the kernel enter stack frame
   * (see kernel enter stack frame documented in tpl_system_call.S) */
  str r2, [sp, #16]

  /* returns with no error */
  mov r0, #E_OK
  mov pc, lr

cracker_in_action:
  /* we do nothing (but returning an error) ! */
  mov r0, #E_OS_PROTECTION_EXCEPTION
  mov pc, lr

#else /* if TRUSTED_FCT_COUNT == 0 */
  mov r0, #E_OS_SERVICEID
  mov pc, lr
#endif /* TRUSTED_FCT_COUNT == 0 */
#else /* WITH_MEMORY_PROTECTION != YES */
  mov r0, #E_OS_SERVICEID
  mov pc, lr
#endif /* WITH_MEMORY_PROTECTION != YES */

#define OS_STOP_SEC_CODE
#include "tpl_as_memmap.h"
